<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  

  
  <title>UI 为什么必须要在主线程更新 | 山芋头</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="在iOS开发中，一直知道不能再子线程中更新UI，但是被面试官问到为什么不能再子线程中更新UI,却不知道了尴尬，这就是学习不探究原理的的毛病，要治。 UIKit 官方文档中有重点提示：  Important Use UIKit classes only from your app’s main thread or main dispatch queue, unless otherwise indic">
<meta property="og:type" content="article">
<meta property="og:title" content="UI 为什么必须要在主线程更新">
<meta property="og:url" content="https://bianchengme.gitee.io/2019/03/01/UI-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BF%85%E9%A1%BB%E8%A6%81%E5%9C%A8%E4%B8%BB%E7%BA%BF%E7%A8%8B%E6%9B%B4%E6%96%B0/index.html">
<meta property="og:site_name" content="山芋头">
<meta property="og:description" content="在iOS开发中，一直知道不能再子线程中更新UI，但是被面试官问到为什么不能再子线程中更新UI,却不知道了尴尬，这就是学习不探究原理的的毛病，要治。 UIKit 官方文档中有重点提示：  Important Use UIKit classes only from your app’s main thread or main dispatch queue, unless otherwise indic">
<meta property="og:locale" content="cn">
<meta property="og:image" content="https://bianchengme.gitee.io/2019/03/01/UI-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BF%85%E9%A1%BB%E8%A6%81%E5%9C%A8%E4%B8%BB%E7%BA%BF%E7%A8%8B%E6%9B%B4%E6%96%B0/uikit_stack.png">
<meta property="og:image" content="https://bianchengme.gitee.io/2019/03/01/UI-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BF%85%E9%A1%BB%E8%A6%81%E5%9C%A8%E4%B8%BB%E7%BA%BF%E7%A8%8B%E6%9B%B4%E6%96%B0/core_animation_pipeline.png">
<meta property="article:published_time" content="2019-03-01T11:58:22.000Z">
<meta property="article:modified_time" content="2020-07-28T05:41:08.579Z">
<meta property="article:author" content="jmme@foxmail.com">
<meta property="article:tag" content="iOS">
<meta property="article:tag" content="UI">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://bianchengme.gitee.io/2019/03/01/UI-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BF%85%E9%A1%BB%E8%A6%81%E5%9C%A8%E4%B8%BB%E7%BA%BF%E7%A8%8B%E6%9B%B4%E6%96%B0/uikit_stack.png">
  
    <link rel="alternate" href="/atom.xml" title="山芋头" type="application/atom+xml">
  
  
    <link rel="icon" href="/favicon.png">
  
  
    <link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
  
  
<link rel="stylesheet" href="/css/style.css">

<meta name="generator" content="Hexo 4.2.0"></head>

<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/" id="logo">山芋头</a>
      </h1>
      
        <h2 id="subtitle-wrap">
          <a href="/" id="subtitle">记录和分享能创造价值</a>
        </h2>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
          <a class="main-nav-link" href="/">Home</a>
        
          <a class="main-nav-link" href="/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
          <a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
        
        <a id="nav-search-btn" class="nav-icon" title="Search"></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="https://bianchengme.gitee.io"></form>
      </div>
    </div>
  </div>
</header>
      <div class="outer">
        <section id="main"><article id="post-UI-为什么必须要在主线程更新" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2019/03/01/UI-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BF%85%E9%A1%BB%E8%A6%81%E5%9C%A8%E4%B8%BB%E7%BA%BF%E7%A8%8B%E6%9B%B4%E6%96%B0/" class="article-date">
  <time datetime="2019-03-01T11:58:22.000Z" itemprop="datePublished">2019-03-01</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/iOS/">iOS</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 class="article-title" itemprop="name">
      UI 为什么必须要在主线程更新
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>在iOS开发中，一直知道不能再子线程中更新UI，但是被面试官问到为什么不能再子线程中更新UI,却不知道了尴尬，这就是学习不探究原理的的毛病，要治。</p>
<p>UIKit 官方文档中有重点提示： </p>
<p><strong>Important</strong></p>
<p>Use UIKit classes only from your app’s main thread or main dispatch queue, unless otherwise indicated. This restriction particularly applies to classes derived from UIResponder or that involve manipulating your app’s user interface in any way.</p>
<p>意思就是只能在主线程或者主队列中使用UIKit类，特别适用于派生自UIPResponder的类，或者涉及到以任何方式操纵应用程序的用户界面。</p>
<p>我们探究背后的原因是什么？</p>
<p>UIKit并不是一个线程安全的类，UI操作涉及到渲染访问各种View对象的属性，如果异步操作下会存在读写问题，而为其加锁则会耗费大量资源并拖慢运行速度。另一方面因为整个程序的起点UIApplication是在主线程进行初始化，所有的用户事件都是在主线程上进行传递（如点击、拖动），所以view只能在主线程上才能对事件进行响应。而在渲染方面由于图像的渲染需要以60帧的刷新率在屏幕上同时更新，在非主线程异步化的情况下无法确定这个处理过程能够实现同步更新。</p>
<p><strong>UIKit非线程安全</strong></p>
<p>在UIKit中，很多类中大部分的属性都被修饰为nonatomic，这意味着它们不能在多线程的环境下工作，而对于UIKit这样一个庞大的框架，将其所有属性都设计为线程安全是不现实的，这可不仅仅是简单的将nonatomic改成atomic或者是加锁解锁的操作，还涉及到很多的方面：</p>
<p>假设能够异步设置view的属性，那我们究竟是希望这些改动能够同时生效，还是按照各自runloop的进度去改变这个view的属性呢？</p>
<p>假设UITableView在其他线程去移除了一个cell，而在另一个线程却对这个cell所在的index进行一些操作，这时候可能就会引发crash。</p>
<p>如果在后台线程移除了一个view，这个时候runloop周期还没有完结，用户在主线程点击了这个“将要”消失的view，那么究竟该不该响应事件？在哪条线程进行响应？</p>
<p>仔细思考，似乎能够多线程处理UI并没有给我们开发带来更多的便利，假如你代入了这些情景进行思考，你很容易得出一个结论： “我在一个串行队列对这些事件进行处理就可以了。” 苹果也是这样想的，所以UIKit的所有操作都要放到主线程串行执行。</p>
<p>在Thread-Safe Class Design一文提到：</p>
<p>It’s a conscious design decision from Apple’s side to not have UIKit be thread-safe. Making it thread-safe wouldn’t buy you much in terms of performance; it would in fact make many things slower. And the fact that UIKit is tied to the main thread makes it very easy to write concurrent programs and use UIKit. All you have to do is make sure that calls into UIKit are always made on the main thread.</p>
<p>大意为把UIKit设计成线程安全并不会带来太多的便利，也不会提升太多的性能表现，甚至会因为加锁解锁而耗费大量的时间。事实上并发编程也没有因为UIKit是线程不安全而变得困难，我们所需要做的只是要确保UI操作在主线程进行就可以了。</p>
<p><strong>Runloop 与绘图循环</strong></p>
<p>道理我们都懂，那这个究竟跟我们不能在后台线程操作UI有什么关系呢？</p>
<p>UIApplication在主线程所初始化的Runloop我们称为Main Runloop，它负责处理app存活期间的大部分事件，如用户交互等，它一直处于不断处理事件和休眠的循环之中，以确保能尽快的将用户事件传递给GPU进行渲染，使用户行为能够得到响应，画面之所以能够得到不断刷新也是因为Main Runloop在驱动着。</p>
<p>而每一个view的变化的修改并不是立刻变化，相反的会在当前run loop的结束的时候统一进行重绘，这样设计的目的是为了能够在一个runloop里面处理好所有需要变化的view，包括resize、hide、reposition等等，所有view的改变都能在同一时间生效，这样能够更高效的处理绘制，这个机制被称为<strong>绘图循环（View Drawing Cycle)</strong>。</p>
<h2 id="理解iOS的渲染流程"><a href="#理解iOS的渲染流程" class="headerlink" title="理解iOS的渲染流程"></a>理解iOS的渲染流程</h2><h3 id="渲染系统框架"><a href="#渲染系统框架" class="headerlink" title="渲染系统框架"></a>渲染系统框架</h3><p><img src="uikit_stack.png" alt="uikit_stack.png"></p>
<ul>
<li>UIKit: 包含各种控件，负责对用户操作事件的响应，本身并不提供渲染的能力</li>
<li>Core Animation: 负责所有视图的绘制、显示与动画效果</li>
<li>OpenGL ES: 提供2D与3D渲染服务</li>
<li>Core Graphics: 提供2D渲染服务</li>
<li>Graphics Hardware: 指GPU</li>
</ul>
<p>所以在iOS中，所有视图的现实与动画本质上是由 Core Animation 负责，而不是UIKit。</p>
<h3 id="Core-Animation-Pipeline-流水线"><a href="#Core-Animation-Pipeline-流水线" class="headerlink" title="Core Animation Pipeline 流水线"></a>Core Animation Pipeline 流水线</h3><p><img src="core_animation_pipeline.png" alt="uikit_stack.png"></p>
<p>Core Animation的绘制是通过Core Animation Pipeline实现，它以流水线的形式进行渲染，具体分为四个步骤：</p>
<ul>
<li>Commit Transaction:</li>
</ul>
<p>可以细分为</p>
<pre><code>- Layout: 构建视图布局如addSubview等操作

- Display: 重载drawRect:进行时图绘制，该步骤使用CPU与内存

- Prepare: 主要处理图像的解码与格式转换等操作

- Commit: 将Layer递归打包并发送到Render Server</code></pre><ul>
<li><p>Render Server:</p>
<p>  负责渲染工作，会解析上一步Commit Transaction中提交的信息并反序列化成渲染树（render tree)，随后根据layer的各种属性生成绘制指令，并在下一次VSync信号到来时调用OpenGL进行渲染。</p>
</li>
<li><p>GPU:</p>
<p>  GPU会等待显示器的VSync信号发出后才进行OpenGL渲染管线，将3D几何数据转化成2D的像素图像和光栅处理，随后进行新的一帧的渲染，并将其输出到缓冲区。</p>
</li>
<li><p>Dispaly:</p>
<p>  从缓冲区中取出画面，并输出到屏幕上。</p>
</li>
</ul>
<h3 id="知识补充：iOS的VSync与双缓冲机制"><a href="#知识补充：iOS的VSync与双缓冲机制" class="headerlink" title="知识补充：iOS的VSync与双缓冲机制"></a>知识补充：iOS的VSync与双缓冲机制</h3><h4 id="VSync"><a href="#VSync" class="headerlink" title="VSync:"></a>VSync:</h4><p>VSync（vertical sync）是指垂直同步，在玩游戏的时候在设置的时候应该会看见过这个选项，这个机制能够让显卡和显示器保持在一个相同的刷新率从而避免画面撕裂。在iOS中，屏幕具有60Hz的刷新率，这意味着它每秒需要显示60张不同的图片（帧），但GPU并没有一个确定的刷新率，在某些时候GPU可能被要求更强力的数据输出来确保渲染能力，这时候他们可能比屏幕刷新率（60Hz）更快，就会导致屏幕不能完整的渲染所有GPU给他的数据，因为它不够快，屏幕的上一帧还没渲染完，下一帧就已经到来了，这就导致画面的撕裂。</p>
<p>这个时候我们就要引入VSync了，简单来说它就是让显卡保持他的输出速率不高于屏幕的刷新率，启用了VSync后，GPU不再会给你可怜的60Hz屏幕每秒发送100帧了，它会增加每一帧的发送间隔，确保显示器能够有充足的时间去处理每一帧。</p>
<h4 id="双缓冲机制"><a href="#双缓冲机制" class="headerlink" title="双缓冲机制"></a>双缓冲机制</h4><p>双缓冲机制是用于避免或减少画面闪烁的问题，在单缓冲的情况下，GPU输出了一帧画面，缓冲区就需要马上获取这个画面，并交给显示屏去显示，而这段时间GPU输出的画面就全都丢失了，因为没有缓冲区去承载这些画面，就会造成画面的闪烁。</p>
<p>而在双缓冲机制下有一个Back Frame Buffer和一个Front Frame Buffer，在GPU绘制完成后，它会将图像先保存到Back Frame Buffer中，操作完毕后，会调用一个交换函数，让绘制完成的Back Frame Buffer上的图像交换到Front Frame Buffer上。由于双缓冲利用了更多显存与CPU消耗时间，从而避免了画面的闪烁。</p>
<p>相信大家都会遇到过应用卡顿，卡顿的原因就是因为两帧的刷新时间间隔大于60帧每秒（约16.67ms），导致用户感觉点击或者滑动时，界面没有及时的响应。</p>
<p>前面提到Core Animation Pipeline是以流水线的形式工作的，在理想的状况下我们希望它能够在1/60s内完成图层树的准备工作并提交给渲染进程，而渲染进程在下一次VSync信号到来的时候提交给GPU进行渲染，并在1/60s内完成渲染，这样就不会产生任何的卡顿。</p>
<p>但是由于我们使用了我们的魔法UIKit，所以我们在许多后台线程进行了UI操作，在runloop的结尾准备进行渲染的时候，不同线程提交了不同的渲染信息，于是我们就拥有了更多的绘制事务，这个时候Core Animation Pipeline会不断将信息提交，让GPU进行渲染，由于绘制事件的不同步导致了GPU渲染的不同步，可能在上一帧是需要渲染一个label消失的画面，下一帧却又需要渲染这个label改变了文字，最终导致的是界面的不同步。</p>
<p>另一方面，在VSync和双缓冲机制我们可以看出渲染其实是一个十分消耗系统资源的操作（占用显存与CPU），所以可能会因为大量的事务和线程之间频繁的上下文切换导致了GPU无法处理，反而影响了性能，从而导致在1/60s中无法完成图层树的提交，导致了严重的卡顿。</p>
<h4 id="Texture-or-ComponentKit-实现支持异步更新UI框架"><a href="#Texture-or-ComponentKit-实现支持异步更新UI框架" class="headerlink" title="Texture or ComponentKit 实现支持异步更新UI框架"></a>Texture or ComponentKit 实现支持异步更新UI框架</h4><ul>
<li><p>AsyncDisplayKit（现命名为Texture） 是Facebook开源的一个用于保持iOS界面流畅的框架。</p>
</li>
<li><p>ComponentKit是Facebook开源的一个基于React思想的iOS原生UI开发框架。它通过函数式和声明的方式构建UI。</p>
</li>
</ul>
<p>让我们撤销掉我们对UIKit施展的各种魔法，回到这个UI只能在主线程进行操作的世界吧。这两个框架其实并不是真正的在后台线程操作UI，而是用了更巧妙的方法将一些耗时的操作异步执行，从而绕开了UIKit只能在主线程操作的限制。</p>
<p>比如Texture创建了各类Node，在node中包含了UIView，而Node本身是线程安全的，所以允许在后台线程对Node进行修改，随后在第一次主线程访问View的时候它才会在内部生成对应的View，当node的属性发生改变的时候，他也不会马上进行修改，而是在适当的时机一次性的在主线程为内部的View进行设置。（有点类似于绘图循环）</p>
<p>而ComponentKit则是通过创建Component来描述UI，它也是一个线程安全的类。可以将Component认为是一个刻板，而UIView是刻板下的一张纸，渲染则是喷墨的过程。当我们生成了一个Component的时候，就等于生成了一个View的模版，在进行渲染的时候只要按照模版进行绘制就可以了。复杂的界面可以通过各种简单的Component来组成。（类似于Flutter的widget）</p>
<h4 id="子线程中如何通知主线程（队列）更新UI方法，记录一下"><a href="#子线程中如何通知主线程（队列）更新UI方法，记录一下" class="headerlink" title="子线程中如何通知主线程（队列）更新UI方法，记录一下"></a>子线程中如何通知主线程（队列）更新UI方法，记录一下</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;方法一</span><br><span class="line">[self performSelectorOnMainThread:@selector(updateImage:) withObject:data waitUntilDone:YES];</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;方法二</span><br><span class="line">dispatch_async(dispatch_get_main_queue(), ^&#123;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;更新UI的代码，不用主线程中调用</span><br><span class="line"></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;方法三</span><br><span class="line">[[NSOperationQueue mainQueue] addOperationWithBlock:^&#123;</span><br><span class="line"></span><br><span class="line">NSLog(@&quot;线程2：%@&quot;,[NSThread currentThread]);</span><br><span class="line"></span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure>


      
    </div>
    <footer class="article-footer">
      <a data-url="https://bianchengme.gitee.io/2019/03/01/UI-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BF%85%E9%A1%BB%E8%A6%81%E5%9C%A8%E4%B8%BB%E7%BA%BF%E7%A8%8B%E6%9B%B4%E6%96%B0/" data-id="cktcaskhh001h3gucfbkx3pmn" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/UI/" rel="tag">UI</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/iOS/" rel="tag">iOS</a></li></ul>

    </footer>
  </div>
  
    
<nav id="article-nav">
  
    <a href="/2019/03/05/iOS%E5%B8%B8%E8%A7%81%E7%AE%97%E6%B3%95%E6%B1%87%E6%80%BB/" id="article-nav-newer" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Newer</strong>
      <div class="article-nav-title">
        
          iOS常见算法汇总
        
      </div>
    </a>
  
  
    <a href="/2019/02/26/%E6%8A%8AMac%E4%B8%8A%E5%A5%BD%E7%94%A8%E7%9A%84%E6%93%8D%E4%BD%9C%E6%90%AC%E5%88%B0Windows/" id="article-nav-older" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Older</strong>
      <div class="article-nav-title">把Mac上好用的操作搬到Windows</div>
    </a>
  
</nav>

  
</article>

</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Categories</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/Docker/">Docker</a><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/Docker/Nacos/">Nacos</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Docker/mysql/">mysql</a></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/JAVA/">JAVA</a><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/JAVA/JVM/">JVM</a></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/Markdown/">Markdown</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Windows/">Windows</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/docker/">docker</a><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/docker/redis/">redis</a></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/flutter/">flutter</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/git/">git</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/iOS/">iOS</a><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/iOS/Objective-C/">Objective-C</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/iOS/Xcode/">Xcode</a></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/java/">java</a><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/java/%E5%A4%9A%E7%BA%BF%E7%A8%8B/">多线程</a></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/mysql/">mysql</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/vue/">vue</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tags</h3>
    <div class="widget">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/tags/BUG/" rel="tag">BUG</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Docker/" rel="tag">Docker</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/JAVA/" rel="tag">JAVA</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/JVM/" rel="tag">JVM</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Markdown/" rel="tag">Markdown</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Objective-C/" rel="tag">Objective-C</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/RunTime/" rel="tag">RunTime</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Swift/" rel="tag">Swift</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/UI/" rel="tag">UI</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Windows/" rel="tag">Windows</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Xcode/" rel="tag">Xcode</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/docker/" rel="tag">docker</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/flutter/" rel="tag">flutter</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/" rel="tag">git</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hook/" rel="tag">hook</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/iOS/" rel="tag">iOS</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/iOS%E7%9C%9F%E6%9C%BA%E8%BF%90%E8%A1%8C/" rel="tag">iOS真机运行</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/java/" rel="tag">java</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mysl/" rel="tag">mysl</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mysql/" rel="tag">mysql</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nacos/" rel="tag">nacos</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/redis/" rel="tag">redis</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/volatile/" rel="tag">volatile</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vue/" rel="tag">vue</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%BC%98%E5%8C%96/" rel="tag">优化</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" rel="tag">多线程</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%AD%A6%E4%B9%A0%E8%AE%A1%E5%88%92/" rel="tag">学习计划</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B7%A5%E5%85%B7/" rel="tag">工具</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/" rel="tag">并发编程</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%AE%97%E6%B3%95/" rel="tag">算法</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%BD%91%E7%BB%9C/" rel="tag">网络</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E9%83%A8%E7%BD%B2/" rel="tag">部署</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E9%9D%A2%E8%AF%95/" rel="tag">面试</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tag Cloud</h3>
    <div class="widget tagcloud">
      <a href="/tags/BUG/" style="font-size: 10px;">BUG</a> <a href="/tags/Docker/" style="font-size: 10px;">Docker</a> <a href="/tags/JAVA/" style="font-size: 10px;">JAVA</a> <a href="/tags/JVM/" style="font-size: 10px;">JVM</a> <a href="/tags/Markdown/" style="font-size: 10px;">Markdown</a> <a href="/tags/Objective-C/" style="font-size: 16.67px;">Objective-C</a> <a href="/tags/RunTime/" style="font-size: 10px;">RunTime</a> <a href="/tags/Swift/" style="font-size: 10px;">Swift</a> <a href="/tags/UI/" style="font-size: 13.33px;">UI</a> <a href="/tags/Windows/" style="font-size: 10px;">Windows</a> <a href="/tags/Xcode/" style="font-size: 10px;">Xcode</a> <a href="/tags/docker/" style="font-size: 13.33px;">docker</a> <a href="/tags/flutter/" style="font-size: 13.33px;">flutter</a> <a href="/tags/git/" style="font-size: 10px;">git</a> <a href="/tags/hook/" style="font-size: 10px;">hook</a> <a href="/tags/iOS/" style="font-size: 20px;">iOS</a> <a href="/tags/iOS%E7%9C%9F%E6%9C%BA%E8%BF%90%E8%A1%8C/" style="font-size: 10px;">iOS真机运行</a> <a href="/tags/java/" style="font-size: 16.67px;">java</a> <a href="/tags/mysl/" style="font-size: 10px;">mysl</a> <a href="/tags/mysql/" style="font-size: 10px;">mysql</a> <a href="/tags/nacos/" style="font-size: 10px;">nacos</a> <a href="/tags/redis/" style="font-size: 10px;">redis</a> <a href="/tags/volatile/" style="font-size: 10px;">volatile</a> <a href="/tags/vue/" style="font-size: 10px;">vue</a> <a href="/tags/%E4%BC%98%E5%8C%96/" style="font-size: 10px;">优化</a> <a href="/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" style="font-size: 10px;">多线程</a> <a href="/tags/%E5%AD%A6%E4%B9%A0%E8%AE%A1%E5%88%92/" style="font-size: 10px;">学习计划</a> <a href="/tags/%E5%B7%A5%E5%85%B7/" style="font-size: 10px;">工具</a> <a href="/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/" style="font-size: 10px;">并发编程</a> <a href="/tags/%E7%AE%97%E6%B3%95/" style="font-size: 10px;">算法</a> <a href="/tags/%E7%BD%91%E7%BB%9C/" style="font-size: 10px;">网络</a> <a href="/tags/%E9%83%A8%E7%BD%B2/" style="font-size: 10px;">部署</a> <a href="/tags/%E9%9D%A2%E8%AF%95/" style="font-size: 10px;">面试</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Archives</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/09/">September 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/07/">July 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/06/">June 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/05/">May 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/03/">March 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/10/">October 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/09/">September 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/08/">August 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/07/">July 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/05/">May 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/04/">April 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/03/">March 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/06/">June 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/05/">May 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/04/">April 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/03/">March 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/02/">February 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/10/">October 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/03/">March 2018</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Recent Posts</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/2021/09/07/JVM-%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90%E4%BC%98%E5%8C%96/">JVM 逃逸分析优化</a>
          </li>
        
          <li>
            <a href="/2021/07/02/Nacos%E9%80%9A%E8%BF%87Docke%E9%83%A8%E7%BD%B2/">Nacos通过Docke部署</a>
          </li>
        
          <li>
            <a href="/2021/07/01/Docker%E5%AE%89%E8%A3%85mysql5-7/">Docker安装mysql5.7</a>
          </li>
        
          <li>
            <a href="/2021/07/01/Docker%E5%AE%89%E8%A3%85redis/">Docker安装redis</a>
          </li>
        
          <li>
            <a href="/2021/06/30/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B-volatile%E5%85%B3%E9%94%AE%E5%AD%97%E7%90%86%E8%A7%A3/">Java并发编程-volatile关键字理解</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      &copy; 2021 jmme@foxmail.com<br>
      Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>
    </div>
    <nav id="mobile-nav">
  
    <a href="/" class="mobile-nav-link">Home</a>
  
    <a href="/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>


  
<link rel="stylesheet" href="/fancybox/jquery.fancybox.css">

  
<script src="/fancybox/jquery.fancybox.pack.js"></script>




<script src="/js/script.js"></script>




  </div>
</body>
</html>